﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;

namespace ExportBookmark
{
    /// <summary>
    /// Provides Encryption for serializable objects.
    /// </summary>
    public class Encryption
    {
        private readonly string encryptionKey = "!bookyurl!";
        private readonly byte[] salt;

        #region [ Constructors ]

        /// <summary>
        /// Initializes a new instance of the <see cref="Encryption"/> class.
        /// </summary>
        public Encryption()
        {
            this.salt = Encoding.ASCII.GetBytes(encryptionKey);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Encryption"/> class.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        public Encryption(string key)
        {
            this.encryptionKey = key;
            this.salt = Encoding.ASCII.GetBytes(encryptionKey);
        }

        #endregion

        #region [ Encrypt ]

        /// <summary>
        /// Encrypt the provided value.
        /// </summary>
        /// <param name="toEncrypt">To encrypt.</param>
        /// <returns>
        /// The encrypted string.
        /// </returns>
        //public string Encrypt(object toEncrypt)
        //{
        //    return Encrypt(Serializer.ToJson(toEncrypt));
        //}

        /// <summary>
        /// Encrypts a User object.
        /// </summary>
        /// <param name="username">
        /// The username.
        /// </param>
        /// <param name="password">
        /// The password.
        /// </param>
        /// <returns>
        /// The encrypted string.
        /// </returns>
        //public string Encrypt(string username, string password)
        //{
        //    var user = new User(username, password, DateTime.Now);
        //    return Encrypt(Serializer.ToJson(user));
        //}

        /// <summary>
        ///   Encrypts any string using the Rijndael algorithm.
        /// </summary>
        /// <param name = "inputText">The string to encrypt.</param>
        /// <returns>
        /// A Base64 encrypted string.
        /// </returns>
        public string Encrypt(string inputText)
        {
            var rijndaelCipher = new RijndaelManaged();
            byte[] plainText = Encoding.Unicode.GetBytes(inputText);
            var secretKey = new Rfc2898DeriveBytes(encryptionKey, salt);

            using (var encryptor = rijndaelCipher.CreateEncryptor(rgbKey: secretKey.GetBytes(32), rgbIV: secretKey.GetBytes(16)))
            {
                using (var memoryStream = new MemoryStream())
                {
                    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(plainText, 0, plainText.Length);
                        cryptoStream.FlushFinalBlock();
                        return Convert.ToBase64String(memoryStream.ToArray());
                    }
                }
            }
        }

        #endregion

        #region [ Decrypt ]

        /// <summary>
        /// Decrypt a provided value.
        /// </summary>
        /// <typeparam name="T">Type of object to decrypt</typeparam>
        /// <param name="encryptedValue">The encrypted string</param>
        /// <returns>
        /// Decrypted object of type T
        /// </returns>
        //public T Decrypt<T>(string encryptedValue)
        //    where T : class
        //{
        //    return (T)Serializer.FromJson(Decrypt(encryptedValue), typeof(T));
        //}

        /// <summary>
        ///   Decrypts a previously encrypted string.
        /// </summary>
        /// <param name = "inputText">The encrypted string to decrypt.</param>
        /// <returns>A decrypted string.</returns>
        public string Decrypt(string inputText)
        {
            var rijndaelCipher = new RijndaelManaged();
            var encryptedData = Convert.FromBase64String(inputText);
            var secretKey = new Rfc2898DeriveBytes(encryptionKey, salt);

            using (var decryptor = rijndaelCipher.CreateDecryptor(rgbKey: secretKey.GetBytes(32), rgbIV: secretKey.GetBytes(16)))
            {
                using (var memoryStream = new MemoryStream(encryptedData))
                {
                    using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                    {
                        var plainText = new byte[encryptedData.Length];
                        var decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
                        return Encoding.Unicode.GetString(plainText, 0, decryptedCount);
                    }
                }
            }
        }

        #endregion

        ///// <summary>
        ///// Minimal User class used solely for encrypting
        ///// </summary>
        //[Serializable]
        //[DataContract]
        //public class User
        //{
        //    /// <summary>
        //    /// Gets or sets the username.
        //    /// </summary>
        //    /// <value>
        //    /// The username.
        //    /// </value>
        //    [DataMember]
        //    public string Username { get; set; }

        //    /// <summary>
        //    /// Gets or sets the password.
        //    /// </summary>
        //    /// <value>
        //    /// The password.
        //    /// </value>
        //    [DataMember]
        //    public string Password { get; set; }

        //    /// <summary>
        //    /// Gets or sets the date time.
        //    /// </summary>
        //    /// <value>
        //    /// The date time.
        //    /// </value>
        //    [DataMember]
        //    public DateTime DateTime { get; set; }

        //    /// <summary>
        //    /// Initializes a new instance of the <see cref="User"/> class.
        //    /// </summary>
        //    /// <param name="userName">
        //    /// The user name.
        //    /// </param>
        //    /// <param name="password">
        //    /// The password.
        //    /// </param>
        //    /// <param name="dateTime">
        //    /// The date time.
        //    /// </param>
        //    public User(string userName, string password, DateTime dateTime)
        //    {
        //        Username = userName;
        //        Password = password;
        //        DateTime = dateTime;
        //    }
        //}
    }
}